package com.cefa.finance.utils;

import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collections;
import java.util.List;
import java.util.Map;

public final class SignUtil {

    private static final Logger logger = LoggerFactory.getLogger(SignUtil.class);
    /**
     * 默认的签名密钥
     */
    private static final String DEFAULT_SECRET = "#a@!bcd<>ef@%^_g+-=*(7aweqweqwkjl$32123";

    /**
     * 默认的签名参数名称
     */
    private static final String SIGN_PARAM_NAME = "sign";

    private static final SignMethod SIGN_METHOD = SignMethod.MD5;

    public enum SignMethod {
        MD5, SHA1
    }

    private String signName = null;

    private SignMethod signMethod = null;

    private SignUtil() {
        this(SIGN_PARAM_NAME, SIGN_METHOD);
    }

    private SignUtil(String signName, SignMethod signMethod) {
        this.signName = signName;
        this.signMethod = signMethod;
    }


    public static SignUtil build() {
        return new SignUtil();
    }

    public static SignUtil build(String signName) {
        if (StringUtils.isBlank(signName)) {
            throw new NullPointerException("Sign param name is null");
        }
        return new SignUtil(signName, SIGN_METHOD);
    }

    public static SignUtil build(SignMethod signMethod) {
        if (signMethod == null) {
            throw new NullPointerException("Sign param name is null");
        }
        return new SignUtil(SIGN_PARAM_NAME, signMethod);
    }

    public static SignUtil build(String signName, SignMethod signMethod) {
        if (StringUtils.isBlank(signName) || signMethod == null) {
            throw new NullPointerException("Sign param name is null");
        }
        if (signMethod != SignMethod.MD5 && signMethod != SignMethod.SHA1) {
            throw new IllegalArgumentException("Not support sign method " + signMethod);
        }
        return new SignUtil(signName, signMethod);
    }


    public String sign(Map<String, String> data) {
        return sign(data, DEFAULT_SECRET);
    }


    public String sign(Map<String, String> data, String signSecret) {
        String req = mapToUrlParam(data, signSecret);
        logger.info("验签字符串是:{}", req);
        if (signMethod == SignMethod.MD5) {
            return DigestUtils.md5Hex(req).toUpperCase();
        } else {
            return DigestUtils.sha1Hex(req).toUpperCase();
        }
    }


    public String sign(String data) {
        if (StringUtils.isBlank(data)) {
            return null;
        }
        return sign(data, DEFAULT_SECRET);
    }

    /**
     * @param data       格式如a=1&b=2&c=3
     * @param signSecret 签名key
     * @return
     */
    public String sign(String data, String signSecret) {
        if (StringUtils.isBlank(data)) {
            return null;
        }
        Map<String, String> params = urlParamToMap(data);
        return sign(params, signSecret);
    }


    private boolean validate(String data, String signSecret) {
        if (StringUtils.isBlank(data) || StringUtils.isBlank(signSecret)) {
            return false;
        }
        Map<String, String> params = urlParamToMap(data);
        return validate(params, signSecret);
    }

    public boolean validate(String data) {
        return validate(data, DEFAULT_SECRET);
    }


    public boolean validate(Map<String, String> data) {
        return validate(data, DEFAULT_SECRET);
    }

    /**
     * 验证签名是否正确，data中必须有签名参数
     *
     * @param data
     * @param signSecret
     * @return
     */
    private boolean validate(Map<String, String> data, String signSecret) {
        if (data == null || data.isEmpty() || !data.containsKey(signName)) {
            return false;
        }
        String sign = data.get(signName);
        data.remove(signName);
        return sign.equals(sign(data, signSecret));
    }

    private static String mapToUrlParam(Map<String, String> data, String signSecret) {
        String str = Joiner.on("#").withKeyValueSeparator("=").join(data);
        List<String> list = Lists.newArrayList(Splitter.on("#").trimResults().splitToList(str));
        Collections.sort(list);
        return Joiner.on("&").join(list).concat(signSecret);
    }

    private static Map<String, String> urlParamToMap(String data) {
        if (StringUtils.isBlank(data)) {
            return null;
        }
        Map<String, String> params = Splitter.on("&").omitEmptyStrings().withKeyValueSeparator("=").split(data);
        Map<String, String> m = Maps.newHashMap();
        if (!params.isEmpty()) {
            params.forEach((k, v) -> {
                if (StringUtils.isNotBlank(v)) {
                    m.put(k, v);
                }
            });
        }
        return m;
    }

    public static void main(String[] args) {
        String url = "app_id=miaomore&timestamp=1508404516&uuid=123456";
        String v = SignUtil.build(SignMethod.MD5).sign(url, "7c0d930c$kDL39#lOJ29dM1");
        System.out.println(v);
        System.out.println(urlParamToMap(url));
    }
}